import mapboxgl, { type LngLatLike, type StyleSpecification } from 'mapbox-gl';
import { Base } from './core/base';
import type { Manager } from './core/manager';
import type {
  IManager,
  MaplessOptions,
  MapMode,
  MapStyle,
  Nullable,
} from './index.d';
import Singleton from './Singleton';
import { _2d, _3d } from './theme/2d3d';
import {
  dz_cyber,
  dz_dark,
  dz_light,
  dz_satellite,
  dz_streets,
} from './theme/dzmap';
import { dark, light, satellite, streets } from './theme/mapbox';

export class Mapless extends Base<Mapless> {
  public map: mapboxgl.Map | null = null;

  private onInit: ((e: mapboxgl.Map) => void) | undefined = undefined;
  private onReady: ((e: mapboxgl.Map) => void) | undefined = undefined;
  private onZoom: ((zoom: number) => void) | undefined = undefined;

  public static accessToken =
    'pk.eyJ1Ijoic2t5bWlrdSIsImEiOiJjbHZ4NmlkcDUwNWVnMmxuMXp0Z2dkcGFrIn0.ukQ-u2OSqaWF9k6DYwQ6Zw';

  private _mapMode: MapMode = '2d';
  private _mapStyle: MapStyle | null = null;

  constructor(container: string | HTMLElement, options: MaplessOptions) {
    super();

    this.onInit = options.onMapInit;
    this.onReady = options.onMapReady;
    this.onZoom = options.onMapZoom;

    this.initializeMap(container, options);
  }

  // 初始化地图
  private initializeMap(
    container: string | HTMLElement,
    options: MaplessOptions,
  ) {
    mapboxgl.accessToken = options.accessToken || Mapless.accessToken;

    this._mapStyle = options.style as MapStyle;
    const styleUrl = this.getStyleUrl(options.style || 'streets');

    this.map = new mapboxgl.Map({
      container: container,
      style: styleUrl as StyleSpecification,
      center: options.center || [0, 0],
      zoom: options.zoom || 9,
      pitch: 0,
      bearing: 0,
      preserveDrawingBuffer: true, // 允许从WebGL canvas读取像素数据，用于截图功能
      // TODO fuck Propagation
      transformRequest: (url) => {
        if (url.includes('/api/v2/Propagation/Wms')) {
          const token = '???';
          return {
            url,
            headers: {
              Authorization: `Bearer ${token}`, // 替换成你的 token
            },
          };
        }
        return { url };
      },
    });

    this.map.showTileBoundaries = true;

    this.map.on('zoom', this.mapZoomHandler);
    this.map.on('load', this.initCtrls);

    Promise.resolve().then(() => {
      this.map && this.onInit?.(this.map);
    });

    const mapReady = Promise.all([
      new Promise((resolve) => this.map?.on('load', resolve)),
      new Promise((resolve) => this.map?.on('style.load', resolve)),
      Singleton.loadDefaultIcons(this.map),
    ]);
    mapReady
      .then(() => {
        this.map && this.onReady?.(this.map);
      })
      .catch((rej) => {
        console.log(rej);
      });

    // this.map.on('error', (e) => {
    //   const { error } = e;
    //   if ((error as Error & { status: number }).status === 400) {
    //     console.warn('瓦片加载失败:', e.error);
    //     // 回退到上一层级
    //     // this.map?.setZoom(Math.max(0, this.map.getZoom() - 1));
    //     // 可选：显示一个友好的提示
    //     // alert('当前层级无地图数据，已自动调整缩放级别');
    //   }
    // });
  }

  private initCtrls = async () => {
    if (this.map) {
      this.map.addControl(new mapboxgl.ScaleControl(), 'bottom-left');
      this.map.addControl(new mapboxgl.NavigationControl(), 'bottom-right');
      // this.map.addControl(new mapboxgl.FullscreenControl(), 'top-right');
      // this.map.addControl(new mapboxgl.GeolocateControl(), 'top-right');
      // this.map.addControl(new mapboxgl.AttributionControl(), 'top-right');
      // 隐藏左下角logo
      // this.map._logoControl && this.map.removeControl(this.map._logoControl);
      const logo = this.map.getContainer().querySelector('.mapboxgl-ctrl-logo');
      if (logo) {
        (logo as HTMLElement).style.display = 'none';
      }
      // 隐藏右下角链接
      const attributions = this.map
        .getContainer()
        .querySelector('.mapboxgl-ctrl-attrib');
      if (attributions) (attributions as HTMLElement).style.display = 'none';
      // 绘图工具 @mapbox/mapbox-gl-draw {@link ./draw/index.ts}
      // 语言工具 @mapbox/mapbox-gl-language {@link ./lang/index.ts}
    }
  };

  private mapZoomHandler = () => {
    this.managers?.forEach((manager) => {
      manager.mapZoomHandler?.();
    });
    // 获取当前缩放级别
    const currentZoom = this.map?.getZoom();
    currentZoom && this.onZoom?.(currentZoom);
  };

  // 获取预定义样式URL
  private getStyleUrl(style: MapStyle): unknown {
    // const styles = {
    //   dark: 'mapbox://styles/mapbox/dark-v11',
    //   light: 'mapbox://styles/mapbox/light-v11',
    //   satellite: 'mapbox://styles/mapbox/satellite-v9',
    //   streets: 'mapbox://styles/mapbox/streets-v12',
    // };
    // TODO deepClone or {...}
    const styles = JSON.parse(
      JSON.stringify({
        dark,
        light,
        satellite,
        streets,
        dz_dark,
        dz_light,
        dz_satellite,
        dz_cyber,
        dz_streets,
      }),
    );
    const ret = styles[style as keyof typeof styles] || style;
    return ret;
  }

  async loadSvg(svgString: string): Promise<void> {
    return new Promise((resolve) => {
      const blob = new Blob([svgString], { type: 'image/svg+xml' });
      const url = URL.createObjectURL(blob);

      this.map?.loadImage(url, (error, image) => {
        if (error) throw error;
        if (!image) return;
        this.map?.addImage('dynamic-svg-icon', image);
        resolve();
        // test
        // 在图层中使用
        this.map?.addLayer({
          id: 'svg-layer',
          type: 'symbol',
          layout: { 'icon-image': 'dynamic-svg-icon' },
        });
      });
    });
  }

  isDark(s?: string) {
    const res = s || this._mapStyle;
    return (
      res === 'dark' ||
      res === 'dz_dark' ||
      res === 'dz_cyber' ||
      res === 'dz_satellite' ||
      res === 'satellite'
    );
  }

  // 地图操作封装
  resetNorth() {
    this.map?.easeTo({ bearing: 0, pitch: 0 });
  }

  // 设置地图显示范围以包含所有给定坐标点
  fitBounds(coordinates: [number, number][], padding = 20) {
    if (!this.map || !coordinates.length) return;

    const bounds = coordinates.reduce(
      (bounds, coord) => bounds.extend(coord),
      new mapboxgl.LngLatBounds(coordinates[0], coordinates[0]),
    );
    try {
      this.map.fitBounds(bounds, {
        padding: padding,
        duration: 1000,
      });
    } catch (error) {
      console.error('fitBounds error:', error);
    }
  }

  toggleMode(mode: '2d' | '3d' | MapMode) {
    if (!this.map) return;

    this._mapMode = mode;

    const layersVisibility = mode === '2d' ? _2d : _3d;
    layersVisibility.hide.forEach((layer) => {
      if (this.map?.getLayer(layer)) {
        this.map?.setLayoutProperty(layer, 'visibility', 'none');
      }
    });
    layersVisibility.show.forEach((layer) => {
      if (this.map?.getLayer(layer)) {
        this.map?.setLayoutProperty(layer, 'visibility', 'visible');
      }
    });

    this.map.flyTo(layersVisibility.flyTo);
  }

  zoomTo(level: number) {
    this.map?.zoomTo(level);
  }

  async setStyle(
    style: MapStyle,
    langHandle?: (style: StyleSpecification) => StyleSpecification | undefined,
  ) {
    if (!this.map) return;

    // TODO keepSpecialLayers(prefix:"dzmap-")
    const specialStyle = this.keepSpecialLayers();
    const newStyle = this.getStyleUrl(style) as StyleSpecification;
    // 合并数据源和图层
    newStyle.sources = { ...newStyle.sources, ...specialStyle.sources };
    if (specialStyle.layers) {
      newStyle.layers = [
        ...newStyle.layers,
        ...specialStyle.layers.filter(
          (l) => !newStyle.layers.some((nl) => nl.id === l.id),
        ),
      ];
    }

    this.map.setStyle(langHandle?.(newStyle) || newStyle);

    // TODO 非json导入的style之间互相切换，不会引发style.load
    // 暂：采用langHandle由外部解决这个问题

    this.managers.forEach((manager) => {
      const mana = manager as IManager;
      if (typeof mana.onStyleChanged === 'function') {
        mana.onStyleChanged?.(style);
      }
    });

    // 样式加载完成后重新加载图标和图层
    this.map.once('style.load', async () => {
      console.log('??????????????????');
      // 重新加载自定义图标
      await Singleton.loadDefaultIcons(this.map);
      // 通知所有管理器重新加载
      // this.managers.forEach((manager) => {
      //   const mana = manager as IManager;
      //   if (typeof mana.onStyleChanged === 'function') {
      //     mana.onStyleChanged?.(style);
      //   }
      // });
    });

    // 保持2d|3d状态
    this.toggleMode(this._mapMode);
  }

  keepSpecialLayers = (prefix = 'dzmap-') => {
    if (!this.map) return {};
    const currentStyle = this.map.getStyle();
    // 只合并以dzmap-开头的源
    const sources = {
      ...Object.fromEntries(
        Object.entries(currentStyle.sources).filter(([id]) =>
          id.startsWith(prefix),
        ),
      ),
    };
    // 只合并以dzmap-开头的图层
    const layers = [
      ...currentStyle.layers.filter((l) => l.id.startsWith(prefix)),
    ];
    return { sources, layers };
  };

  flyTo(options: {
    center: [number, number] | LngLatLike;
    zoom?: number;
    bearing?: number;
    pitch?: number;
    speed?: number;
    curve?: number;
    duration?: number;
    essential?: boolean;
  }) {
    if (!this.map) return;

    this.map.flyTo({
      center: options.center,
      zoom: options.zoom || this.map.getZoom(),
      bearing: options.bearing || 0,
      pitch: options.pitch || 0,
      speed: options.speed || 1.2,
      curve: options.curve || 1.42,
      duration: options.duration || 2000,
      essential: options.essential || true,
    });
  }

  toggleLayer(layerId: string, visible?: boolean) {
    if (!this.map || !this.map.getLayer(layerId)) return;
    const isVisible =
      visible !== undefined
        ? visible
        : this.map?.getLayoutProperty(layerId, 'visibility') !== 'none';
    this.map?.setLayoutProperty(
      layerId,
      'visibility',
      isVisible ? 'visible' : 'none',
    );
  }

  interactionLayers(interaction: boolean) {
    if (!this.map) return;
    this.managers.forEach((manager) => {
      const mana = manager as Manager;
      mana?.setInteraction(interaction);
    });
  }

  setOpacity(opacity: number) {
    if (!this.map) return;
    const canvas = this.map?.getCanvas();
    if (!canvas) return;
    canvas.style.transition = 'all 0.3s ease-in-out';
    canvas.style.opacity = `${opacity}`;
    canvas.style.filter = `blur(${2 - opacity * 2}px)`;
  }

  showMask(_show: boolean) {
    if (!this.map) return;
    // TODO 使用mask代替setOpacity
  }

  resize() {
    this.map?.resize();
  }

  hasIcon(name: string): boolean {
    return !!this.map?.hasImage(name);
  }

  getIconUrl(name: string): Nullable<string> {
    return Singleton.getIconUrl(name);
  }

  moveLayer(layerId: string, beforeLayerId: string) {
    if (this.map?.getLayer(layerId) && this.map?.getLayer(beforeLayerId)) {
      this.map?.moveLayer(layerId, beforeLayerId);
    }
  }

  moveLayers(layerId: string, beforeLayerIds: string[]) {
    const styleLayers = this.map?.getStyle().layers || [];
    let bottomLayerId = beforeLayerIds[0];
    for (const id of beforeLayerIds) {
      const currentIndex = styleLayers.findIndex((l) => l.id === id);
      const bottomIndex = styleLayers.findIndex((l) => l.id === bottomLayerId);
      if (currentIndex > -1 && bottomIndex > -1 && currentIndex < bottomIndex) {
        bottomLayerId = id;
      }
    }
    this.moveLayer(layerId, bottomLayerId);
  }

  moveManagerLayers(mana: IManager, beforeMana: IManager | undefined) {
    if (!beforeMana) return;
    for (const layerId of mana.getLayerIds()) {
      this.moveLayers(layerId, beforeMana.getLayerIds());
    }
  }

  // 清理资源
  destroy() {
    if (!this.map) return;
    // 清理所有注册的组件
    this.clear();
    this.map.remove();
    this.map = null;
  }
}

export default Mapless;

export type { MapMode };
